#include "language.h"
#include "syntax_call.h"
#include "parse_info.h"
#include <iostream>

using namespace std;

namespace syntax_analysis
{
	syntax_type syntax_call::get_type()
	{
		return SYNTAX_CALL;
	}
	
	string syntax_call::to_string()
	{
		string syn;
		syn += "{";
		syn += m_name;
		syn += "}";
		if( m_attr != NULL )
			syn += m_attr->to_string();
		return syn;
	}
	
	bool syntax_call::reparse( parse_info *info )
	{
		parse_para *para;
		parse_info *child_info;
		parse_para *child_para;
		bool result = false;
		syntax *syn;
		
		para = info->get_parse_para();
		if( para == NULL )
			return false;
		
		if( info->get_child().size() > 1 ||
			para->get_acceptted_count() > 0 && para->get_acceptted_syn( para->get_acceptted_count()-1 ) != this )
			debug_info( info );

		inc_called();
		para->push_call( this );
		
		child_info = info->get_child( 0 );
		
		if( child_info != NULL )
		{
			child_para = child_info->get_parse_para();
			syn = child_para->get_acceptted_syn( child_para->get_acceptted_count() - 1 );
			if( syn != NULL )
				result = syn->reparse( child_info );
			if( result )
			{
				parse_para *tmp;
				int acceptted;
				tmp = child_info->get_parse_para();
				acceptted = tmp->get_last_start_code() - para->get_code();
				if( m_attr == NULL || (m_attr->get_match_type()&DONT_TAKE_STRING) == 0 )
				{
					if( info->get_child().size() != 1 )
						debug_info( info );

					para->remove_last_acceptted();
					result = para->add_acceptted( this, acceptted );
				}
			}
		}

		if( !result && can_accept_more( para ) && child_info != NULL )
			result = do_parse( para, child_info );

		if( !result )
		{
			para->get_language()->set_error( para->get_callstack(), para->get_last_start_code() );
			para->pop_call();
		}
		else
		{
			para->pop_call();
		}
		
		para->set_parsed();

		return result;
	}

	bool syntax_call::parse( parse_info *info )
	{
		parse_para *para;
		parse_info *child_info;
		bool result = false;

		para = info->get_parse_para();
		if( para == NULL )
			return false;
		
		inc_called();
		para->push_call( this );

		info->clear_child();

		if( should_accept_more( para ) )
		{
			do 
			{
				child_info = info->get_child( 0 );
				if( child_info == NULL )
					child_info = info->create_child( para->get_last_start_code(), para->get_language() );

				result = do_parse( para, child_info );

			} while( result && should_accept_more( para ) );
		}
		else if( !para->parsed() )
		{
			child_info = info->create_child( para->get_last_start_code(), para->get_language() );
			result = true;
		}

		if( result && ( 
			m_attr == NULL || 
			(m_attr->get_match_type()&MAX_MATCH) == MAX_MATCH ) )
		{
			while( can_accept_more( para ) && do_parse( para, child_info ) )
			{
				if( m_attr != NULL && (m_attr->get_match_type()&DONT_TAKE_STRING) == DONT_TAKE_STRING )
					break;
			}
		}

		if( !result )
		{
			para->get_language()->set_error( para->get_callstack(), para->get_last_start_code() );
			para->pop_call();
		}
		else
		{
			para->pop_call();
		}

		para->set_parsed();

		return result;
	}
	
	bool syntax_call::do_parse( parse_para *para, parse_info *child_info )
	{
		syntax *syn;
		int nth_syn = 0;
		bool result = false;

		while( !result )
		{
			syn = para->get_language()->get_syntax( m_name, nth_syn );
			if( syn == NULL )
				break;
			result = syn->parse( child_info );
			++nth_syn;
		}
		
		if( result )
		{
			parse_para *tmp;
			int acceptted;
			tmp = child_info->get_parse_para();
			acceptted = tmp->get_last_start_code() - para->get_code();
			if( m_attr == NULL || (m_attr->get_match_type()&DONT_TAKE_STRING) == 0 )
				result = para->add_acceptted( this, acceptted );
			else
				result = para->add_acceptted( this, para->get_acceptted( para->get_acceptted_count() - 1 ), false );
		}
		return result;
	}

	bool syntax_call::can_accept_more( parse_para *para )
	{
		int nmax;
		int real = para->get_acceptted_count();
		if( m_attr == NULL )
			return real < 1;
		
		nmax = m_attr->get_max();
		if( nmax >= 0 )
			return real < nmax;
		return true;
	}

	bool syntax_call::should_accept_more( parse_para *para )
	{
		int nmin;
		int real = para->get_acceptted_count();
		if( m_attr == NULL )
			return real < 1;
		
		nmin = m_attr->get_min();
		if( nmin >= 0 )
			return real < nmin;
		else if( nmin == EXP_ONE_MORE )
			return real < 1;
		return false;
	}

}
